/***
 * 
 * Copyright (C) 2008 Alessandro La Rosa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: alessandro.larosa@gmail.com
 *
 * Author: Alessandro La Rosa
 */
package br.softaware.view.graphics;

import br.softaware.controller.Controller;
import java.util.Calendar;
import java.util.Date;
import javax.microedition.lcdui.Canvas;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import l10n.L10N;

public class CalendarWidget {

    public static final String[] MONTH_LABELS = new String[]{
        L10N.getMessage("January"), L10N.getMessage("February"),
        L10N.getMessage("March"), L10N.getMessage("April"), L10N.getMessage("May"),
        L10N.getMessage("June"), L10N.getMessage("July"), L10N.getMessage("August"),
        L10N.getMessage("September"), L10N.getMessage("October"),
        L10N.getMessage("November"), L10N.getMessage("December")
    };
    static final String[] WEEKDAY_LABELS = new String[]{
        L10N.getMessage("Mo"), L10N.getMessage("Tu"), L10N.getMessage("We"),
        L10N.getMessage("Th"), L10N.getMessage("Fr"), L10N.getMessage("Sa"),
        L10N.getMessage("Su")
    };

    private Canvas parent;

    /* starting week day: 0 for monday, 6 for sunday */
    public int startWeekday = 0;
    /* elements padding */
    public int padding = 1;
    /* cells border properties */
    public int borderWidth = 1;
    public int borderColor = GraphicsUtil.RGB_BLUE;
    /* weekday labels properties */
    public Font weekdayFont = Font.getDefaultFont();
    public int weekdayBgColor = borderColor;
    public int weekdayColor = 0xffffff;
    /* header (month-year label) properties */
    public Font headerFont = Font.getDefaultFont();
    public int headerBgColor = 0x0000ff;
    public int headerColor = GraphicsUtil.RGB_BLUE;
    /* cells properties */
    public Font font = Font.getDefaultFont();
    public int foreColor = borderColor;
    public int bgColor = 0x9999ff;
    public int selectedBgColor = borderColor;
    public int selectedForeColor = 0xffffff;
    /* internal properties */
    int width = 0;
    int height = 0;
    int headerHeight = 0;
    int weekHeight = 0;
    int cellWidth = 0;
    int cellHeight = 0;
    /* internal time properties */
    long currentTimestamp = 0;
    Calendar calendar = null;
    int weeks = 0;

    DirectionalBtn lastBtn;
    DirectionalBtn nextBtn;

    public int unusedCellsColor = GraphicsUtil.RGB_TRANSPARENT_BLACK;

    private Image weekHeaderImg;

    public CalendarWidget(Canvas parent, Date date) {
        this.parent = parent;
        calendar = Calendar.getInstance();

        lastBtn = new DirectionalBtn(DirectionalBtn.LEFT);
        nextBtn = new DirectionalBtn(DirectionalBtn.RIGHT);
        weekHeaderImg = GraphicsUtil.getInstance().createImage("calendar-week-header.png");

        //we'll see these 2 methods later
        setDate(date);

        initialize();
    }

    public Date getSelectedDate() {
        return calendar.getTime();
    }

    public void setDate(Date d) {
        int prevMonth = calendar.get(Calendar.MONTH);

        currentTimestamp = d.getTime();

        calendar.setTime(d);

        //weeks number can change, depending on week starting day and month total days
        int emptyDays = ((7 - startWeekday) + getStartWeekday()) % 7;
        this.weeks = (int) Math.ceil(((double) emptyDays + getMonthDays()) / 7);

        //we have to check if month has changed
        //if yes, we have to recalculate month height
        //since weeks number could be changed
        if (calendar.get(Calendar.MONTH) != prevMonth) {
            initHeight();
        }
    }

    public void setDate(long timestamp) {
        Date tmpDate = new Date(timestamp);
        if(Controller.getInstance().isValidDate(tmpDate)){
            setDate(tmpDate);
        }
    }

    public void initialize() {
        //let's initialize calendar size
        this.cellWidth = ((int)(parent.getWidth() * 0.95) - 8 * borderWidth) / 7;
        this.cellHeight = (int)(parent.getHeight() * 0.09);

        this.headerHeight = (int)(parent.getHeight() * 0.1);

        //this.weekHeight = (int)(GraphicsUtil.getInstance().getScreenHeight() * 0.08);
        this.weekHeight = weekHeaderImg.getHeight();

        this.width = 7 * (cellWidth + borderWidth) + borderWidth;
        initHeight();
    }

    void initHeight() {
        this.height =
                headerHeight + weekHeight
                + this.weeks * (cellHeight + borderWidth) + borderWidth;
    }

    int getMonthDays() {
        int month = calendar.get(Calendar.MONTH);

        switch (month) {
            case 3:
            case 5:
            case 8:
            case 10:
                return 30;
            case 1:
                return calendar.get(Calendar.YEAR) % 4 == 0 && calendar.get(Calendar.YEAR) % 100 != 0 ? 29 : 28;
            default:
                return 31;
        }
    }

    int getStartWeekday() {
        //let's create a new calendar with same month and year, but with day 1
        Calendar c = Calendar.getInstance();

        c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        c.set(Calendar.DAY_OF_MONTH, 1);

        //we must normalize DAY_OF_WEEK returned value
        return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
    }

    public void keyPressed(int adaptedKeyCode) {
        switch (adaptedKeyCode) {
            case KeyCodeAdapter.UP_KEY:
                go(-7);
                break;
            case KeyCodeAdapter.DOWN_KEY:
                go(7);
                break;
            case KeyCodeAdapter.RIGHT_KEY:
                go(1);
                break;
            case KeyCodeAdapter.LEFT_KEY:
                go(-1);
                break;
        }
    }

    void go(int delta) {
        int prevMonth = calendar.get(Calendar.MONTH);

        setDate(currentTimestamp + 86400000 * delta);

        //we have to check if month has changed
        //if yes, we have to recalculate month height
        //since weeks number could be changed
        if (calendar.get(Calendar.MONTH) != prevMonth) {
            initHeight();
        }
    }

    public void paint(Graphics g) {
        int startX = (parent.getWidth() - width )/ 2;
        g.translate(startX, 0);

        final int arrowGap = 5 + nextBtn.getWidth();
        lastBtn.setX(arrowGap);
        lastBtn.setY(headerHeight/2);
        lastBtn.paint(g);
        nextBtn.setX(width - arrowGap);
        nextBtn.setY(headerHeight/2);
        nextBtn.paint(g);

        //painting header (month-year label)
        g.setFont(headerFont);
        g.setColor(headerColor);
        String header = MONTH_LABELS[calendar.get(Calendar.MONTH)];
        g.drawString(header, width / 2, (headerHeight + headerFont.getHeight()) / 2, Graphics.BOTTOM | Graphics.HCENTER);

        //painting week days labels
        g.translate(0, headerHeight);

        g.setColor(weekdayBgColor);
        //g.fillRect(0, 0, width, weekHeight);
        for (int x = 0; x < width; x++) {
            g.drawImage(weekHeaderImg, x, 0, Graphics.TOP|Graphics.LEFT);
        }

        g.setColor(weekdayColor);
        g.setFont(weekdayFont);

        for (int i = 0; i < 7; i++) {
            g.drawString(WEEKDAY_LABELS[(i + startWeekday) % 7],
                    borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2,
                    (weekHeight + weekdayFont.getHeight()) / 2, Graphics.BOTTOM | Graphics.HCENTER);
        }

        //painting cells borders
        g.translate(0, weekHeight);

        g.setColor(borderColor);

        for (int i = 0; i <= weeks; i++) {
            g.fillRect(0, i * (cellHeight + borderWidth), width, borderWidth);
        }
        for (int i = 0; i <= 7; i++) {
            g.fillRect(i * (cellWidth + borderWidth), 0, borderWidth, height - headerHeight - weekHeight);
        }

        //painting days
        int days = getMonthDays();
        int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;

        //unused cells
        int unusedCellY = borderWidth;
        for (int col = 0; col < dayIndex; col++) {
            int x = borderWidth + col * (cellWidth + borderWidth);
            GraphicsUtil.getInstance().drawTransparentRectangle(g,unusedCellsColor, x, unusedCellY, cellWidth, cellHeight);
        }
        int lastRow = ((dayIndex + (days-1)) / 7);
        int lastWeekday = ((dayIndex + (days-1)) % 7) + 1;
        unusedCellY = borderWidth + lastRow * (cellHeight + borderWidth);
        for (int col = lastWeekday; col < 7; col++) {
            int x = borderWidth + col * (cellWidth + borderWidth);
            GraphicsUtil.getInstance().drawTransparentRectangle(g,unusedCellsColor, x, unusedCellY, cellWidth, cellHeight);
        }

        g.setColor(foreColor);
        g.setFont(font);

        int currentDay = calendar.get(Calendar.DAY_OF_MONTH);

        for (int i = 0; i < days; i++) {
            int weekday = (dayIndex + i) % 7;
            int row = (dayIndex + i) / 7;

            int x = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
            int y = borderWidth + row * (cellHeight + borderWidth);

            //if this is the current day, we'll use selected bg and fore colors
            if (i + 1 == currentDay) {
                g.setColor(selectedBgColor);
                g.fillRect(
                        borderWidth + weekday * (cellWidth + borderWidth),
                        borderWidth + row * (cellHeight + borderWidth),
                        cellWidth, cellHeight);
                g.setColor(selectedForeColor);
            }

            g.drawString("" + (i + 1), x, y + (cellHeight + font.getHeight())/2, Graphics.BOTTOM | Graphics.HCENTER);

            //if this is the current day, we must restore standard fore color
            if (i + 1 == currentDay) {
                g.setColor(foreColor);
            }
        }
        //let's traslate back!
        g.translate(-startX, -headerHeight - weekHeight);
    }

}
